﻿using System;
using System.Net;
using System.IO;
using System.Xml.Linq;
using Lang = SerialManager.Localization.EpisodeLoader;

namespace SerialManager.Loaders
{
    /// <summary>
    /// Abstract class for implementing IEpisodeLoader using WebClient
    /// for remote requests to websites
    /// </summary>
    public abstract class EpisodeWebLoader : EpisodeLoader
    {
        protected static readonly XName XNAnchor = XName.Get( "a" );
        protected static readonly XName XNHref = XName.Get( "href" );
        protected static readonly XName XNTable = XName.Get( "table" );
        protected static readonly XName XNTableRow = XName.Get( "tr" );
        protected static readonly XName XNTableCell = XName.Get( "td" );
        protected static readonly XName XNClass = XName.Get( "class" );

        /// <summary>
        /// Complete address for loading operation
        /// </summary>
        protected abstract Uri Address { get; }

        private WebClient _client;

        /// <summary>
        /// Create and start WebClient asynchronous request
        /// There is also a little thread for indicating delay while for response
        /// </summary>
        public override void Start()
        {
            _client = new WebClient();
            _client.UseDefaultCredentials = true;
            _client.DownloadStringCompleted += new DownloadStringCompletedEventHandler( OnResponse );
            _client.DownloadProgressChanged += new DownloadProgressChangedEventHandler( OnProgress );

            Console.WriteLine( Lang.DownloadStarted );
            Logger.DebugFormat( "Address: {0}", Address );
            Status = EpisodeLoaderStatus.Started;
            
            _client.DownloadStringAsync( Address );
            base.Start();
        }

        /// <summary>
        /// Abort WebClient request
        /// </summary>
        public override void Abort(EpisodeLoaderAbortReason reason)
        {
            _client.CancelAsync();
            base.Abort(reason);
        }

        private void OnProgress( object sender, DownloadProgressChangedEventArgs e )
        {
            Console.Write( "*" );
        }

        private void OnResponse( object sender, DownloadStringCompletedEventArgs e )
        {
            // everything is done in Abort method, just return from here
            if( e.Cancelled )
                return;

            if( e.Error != null )
            {
                Logger.Debug( "Request ended with error", e.Error );
                OnError( e.Error );
                return;
            }
            
            Logger.Debug( "Received response" );

            #region Basic html cleapup and XmlReader creation
            string xml = null;
            StringReader resultReader = null;
            try
            {
                resultReader = new StringReader( e.Result );
                var sgml = new Sgml.SgmlReader();
                sgml.DocType = "html";
                sgml.InputStream = resultReader;
                xml = sgml.ReadOuterXml();
                Logger.Debug( "Prepared response" );
            }
            catch( Exception ex )
            {
                Logger.Debug( "Parsing of response with SgmlReader thrown exception:", ex );
                OnError( ex );
                return;
            }
            finally
            {
                if( resultReader != null )
                    resultReader.Dispose();
            }

            if( Logger.IsDebugEnabled )
            {
                var debugfile = System.IO.Path.Combine( Environment.CurrentDirectory, SerialName + "_debug.xml" );
                using( var debug = System.IO.File.CreateText( debugfile ) )
                    debug.Write( xml );
                Logger.DebugFormat( "Retrieved xml stored in {0}", debugfile );
            }
            #endregion

            #region Creating XElement from received xml
            try
            {
                OnXmlLoaded( XElement.Parse( xml ) );
            }
            catch( Exception xex )
            {
                Logger.Debug( "Creating XDocument thrown exception:", xex );
                OnError( xex );
                return;
            }
            #endregion

            Finished = true;
            Console.WriteLine( " "+Lang.Done );
        }

        protected abstract void OnXmlLoaded( XElement doc );
    }
}
